package com.miao.im.biz;

import com.miao.im.common.Constant;
import com.miao.im.common.Message;
import com.miao.im.common.annotation.MessageCommand;
import com.miao.im.common.enums.Command;
import com.miao.im.common.enums.MessageType;
import com.miao.im.core.AsyncExecutor;
import com.miao.im.dto.AuthRequestDto;
import com.miao.im.dto.SocketMessageDto;
import com.miao.im.model.AuthRequestModel;
import com.miao.im.model.AuthResponseModel;
import com.miao.im.model.KickOutRequestModel;
import com.miao.im.repository.IUserRepository;
import com.miao.im.server.session.SessionContext;
import com.miao.im.server.session.SessionManager;
import com.miao.im.service.auth.IAuthManager;
import com.miao.im.support.handler.AbstractMessageHandler;
import com.miao.im.support.mq.IProducerMessageService;
import com.miao.im.support.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

/**
 * @Auther: miaoguoxin
 * @Date: 2021/3/9 09:44
 * @Description:
 */
@Component
@MessageCommand(value = Command.AUTH, needAuth = false)
@Slf4j
public class AuthRequestHandler extends AbstractMessageHandler<AuthRequestModel.AuthRequest> {
    @Autowired
    @Qualifier("socketAuthManager")
    private IAuthManager<AuthResponseModel.AuthResponse> authManager;
    @Autowired
    private IProducerMessageService messageService;
    @Autowired
    private IUserRepository userRepository;
    @Autowired
    private AsyncExecutor asyncExecutor;

    @Override
    protected void doRead(AuthRequestModel.AuthRequest body, Message message) {
    }

    @Override
    protected Mono<Void> doDispatch(AuthRequestModel.AuthRequest body, Message message, SessionContext sessionContext) {
        //重复认证直接忽略
        if (sessionContext.isAuth()) {
            return Mono.empty();
        }
        log.info("接收到登陆请求:{}", body.getUsername());
        AuthRequestDto requestDto = new AuthRequestDto();
        requestDto.setUsername(body.getUsername());
        requestDto.setPassword(body.getPassword());
        requestDto.setDeviceNumber(body.getDeviceNumber());
        return authManager.reactiveLogin(requestDto)
                .doOnSuccess(authResponse -> {
                    //绑定登录用户信息
                    SessionManager.bindLoginInfo(authResponse, body.getUsername(), sessionContext);
                    //检查重复登陆，剔除该用户
                    this.kickOutOtherDeviceIfNecessary(body.getDeviceNumber(), Long.valueOf(authResponse.getUserId()), sessionContext);
                })
                .doOnSuccess(authResponse -> {
                    Message response = Message.writeResponse(message.getVersion(), message.getSequence(),
                            Command.AUTH.getCode(), authResponse.toByteArray());
                    SessionManager.send(sessionContext, response);
                }).then();
    }

    private void kickOutOtherDeviceIfNecessary(String deviceNumber, Long userId, SessionContext sessionContext) {
        userRepository.reactiveGetOnlineUsers(userId)
                .doOnSuccess(onlineUsers -> {
                    for (AuthResponseModel.AuthResponse onlineUser : onlineUsers) {
                        //设备号相同,结束方法
                        if (deviceNumber.equals(onlineUser.getDeviceNumber())) {
                            continue;
                        }
                        KickOutRequestModel.KickOutRequest kickOut = KickOutRequestModel.KickOutRequest.newBuilder()
                                .setUserId(userId)
                                .setSourceIpAddr(sessionContext.getRemoteNode().getIp())
                                .setSourceDeviceNumber(deviceNumber)
                                .setTargetDeviceNumber(onlineUser.getDeviceNumber())
                                .build();
                        SocketMessageDto messageDto = new SocketMessageDto(Constant.APP_VERSION, MessageType.RES.getCode(), 0L, Command.KICK_OUT.getCode(), kickOut.toByteArray());
                        messageService.send(Constant.DISPATCHER_EXCHANGE, onlineUser.getRoutingKey(), JsonUtil.toJSONStr(messageDto));
                    }
                }).subscribe();
    }

}
